package net.ufrog.common.spring;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import net.ufrog.common.CoreConfig;
import net.ufrog.common.spring.app.SpringWebAppFilter;
import net.ufrog.common.spring.exception.*;
import net.ufrog.common.spring.fastjson.FastJsonpHttpMessageConverter;
import net.ufrog.common.spring.interceptor.PropertiesInterceptor;
import net.ufrog.common.spring.interceptor.PropertiesManager;
import net.ufrog.common.utils.Codecs;
import net.ufrog.common.utils.Strings;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.http.MediaType;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HttpPutFormContentFilter;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

/**
 * 配置简化工具
 *
 * @author ultrafrog, ufrog.net@gmail.com
 * @version 0.1, 2017-07-04
 * @since 0.1
 */
public class SpringConfigurations {

    /** */
    private SpringConfigurations() {}

    /**
     * @param basenames base name string split by ','
     * @return reloadable resource bundle message source
     */
    public static ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource(String basenames) {
        ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
        reloadableResourceBundleMessageSource.setBasenames(Stream.of(basenames.split(",")).map(String::trim).toArray(String[]::new));
        return reloadableResourceBundleMessageSource;
    }

    /**
     * @return reloadable resource bundle message source
     */
    public static ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource() {
        return reloadableResourceBundleMessageSource(CoreConfig.getMessages());
    }

    /**
     * @return session locale resolver
     */
    public static SessionLocaleResolver sessionLocaleResolver() {
        return new SessionLocaleResolver();
    }

    /**
     * @param requestParamKey request param key. the default value is '_request_type'
     * @param defaultRequestType default request type. the default value is 'html'
     * @param errorView error view
     * @param partViewSuffix part view suffix
     * @param jsonView json view
     * @param exceptionLogger exception logger
     * @param exceptionHandlers exception handler array
     * @return exception resolver
     */
    public static ExceptionResolver exceptionResolver(String requestParamKey,
                                                      String defaultRequestType,
                                                      String errorView,
                                                      String partViewSuffix,
                                                      View jsonView,
                                                      ExceptionLogger exceptionLogger,
                                                      ExceptionHandler... exceptionHandlers) {
        ExceptionResolver exceptionResolver = new ExceptionResolver();
        exceptionResolver.setRequestParamKey(requestParamKey);
        exceptionResolver.setDefaultRequestType(defaultRequestType);
        exceptionResolver.setErrorView(errorView);
        exceptionResolver.setPartViewSuffix(partViewSuffix);
        exceptionResolver.setJsonView(jsonView);
        exceptionResolver.setExceptionLogger(exceptionLogger);
        Stream.of(exceptionHandlers).forEach(exceptionResolver.getExceptionHandlers()::add);
        return exceptionResolver;
    }

    /**
     * @param errorView error view
     * @param partViewSuffix part view suffix
     * @param exceptionLogger exception logger
     * @param exceptionHandlers exception handler array
     * @return exception resolver
     */
    public static ExceptionResolver exceptionResolver(String errorView, String partViewSuffix, ExceptionLogger exceptionLogger, ExceptionHandler... exceptionHandlers) {
        return exceptionResolver(null, null, errorView, partViewSuffix, null, exceptionLogger, exceptionHandlers);
    }

    /**
     * @param errorView error view
     * @param partViewSuffix part view suffix
     * @param exceptionLogger exception logger
     * @param notSignView not sign exception view
     * @param notSignUri not sign exception uri
     * @param notSignPrefix not sign exception prefix
     * @return exception resolver
     */
    public static ExceptionResolver exceptionResolver(String errorView, String partViewSuffix, ExceptionLogger exceptionLogger, String notSignView, String notSignUri, String notSignPrefix) {
        ExceptionResolver exceptionResolver = exceptionResolver(errorView, partViewSuffix, exceptionLogger);
        exceptionResolver.getExceptionHandlers().add(new ServiceExceptionHandler());
        exceptionResolver.getExceptionHandlers().add(new InvalidArgumentExceptionHandler());
        exceptionResolver.getExceptionHandlers().add(new NotSignExceptionHandler(notSignView, notSignUri, notSignPrefix));
        return exceptionResolver;
    }

    /**
     * @param lMediaType media type list
     * @param serializerFeatures fastjson serializer feature array
     * @return fastjson http message converter for json & jsonp
     */
    public static FastJsonpHttpMessageConverter fastJsonpHttpMessageConverter(List<MediaType> lMediaType, SerializerFeature... serializerFeatures) {
        FastJsonpHttpMessageConverter fastJsonpHttpMessageConverter = new FastJsonpHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(serializerFeatures);
        fastJsonpHttpMessageConverter.setSupportedMediaTypes(lMediaType);
        fastJsonpHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
        return fastJsonpHttpMessageConverter;
    }

    /**
     * @return fastjson http message converter for json & jsonp
     */
    public static FastJsonpHttpMessageConverter fastJsonpHttpMessageConverter() {
        List<MediaType> lMediaType = new ArrayList<>();
        lMediaType.add(new MediaType("application", "json"));
        lMediaType.add(new MediaType("text", "javascript"));
        return fastJsonpHttpMessageConverter(lMediaType, SerializerFeature.BrowserCompatible, SerializerFeature.DisableCircularReferenceDetect);
    }

    /**
     * @param propertiesManager properties manager
     * @return properties interceptor
     */
    public static PropertiesInterceptor propertiesInterceptor(PropertiesManager propertiesManager) {
        PropertiesInterceptor propertiesInterceptor = new PropertiesInterceptor();
        propertiesInterceptor.setPropertiesManager(propertiesManager);
        return propertiesInterceptor;
    }

    /**
     * @param servletContext servlet context
     * @param annotatedClasses configuration annotated classes
     */
    public static void addContextLoaderListener(ServletContext servletContext, Class<?>... annotatedClasses) {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(annotatedClasses);
        servletContext.addListener(new ContextLoaderListener(context));
    }

    /**
     * @param servletContext servlet context
     * @param urlPatterns url pattern array
     * @param annotatedClasses configuration annotated classes
     */
    public static void addDispatcherServlet(ServletContext servletContext, String[] urlPatterns, Class<?>... annotatedClasses) {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(annotatedClasses);
        ServletRegistration.Dynamic dynamic = servletContext.addServlet(Codecs.uuid(), new DispatcherServlet(context));
        dynamic.setLoadOnStartup(1);
        dynamic.addMapping(urlPatterns);
    }

    /**
     * @param servletContext servlet context
     * @param urlPattern single url pattern
     * @param annotatedClasses configuration annotated classes
     */
    public static void addDispatcherServlet(ServletContext servletContext, String urlPattern, Class<?>... annotatedClasses) {
        addDispatcherServlet(servletContext, Strings.array(urlPattern), annotatedClasses);
    }

    /**
     * @param servletContext servlet context
     * @param urlPatterns url pattern array
     */
    public static void addCharacterEncodingFilter(ServletContext servletContext, String... urlPatterns) {
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding(CoreConfig.getCharset().displayName());
        characterEncodingFilter.setForceEncoding(true);
        servletContext.addFilter(Codecs.uuid(), characterEncodingFilter).addMappingForUrlPatterns(null, false, urlPatterns);
    }

    /**
     * @param servletContext servlet context
     * @param urlPatterns url pattern array
     */
    public static void addHttpPutFormContentFilter(ServletContext servletContext, String... urlPatterns) {
        servletContext.addFilter(Codecs.uuid(), HttpPutFormContentFilter.class).addMappingForServletNames(null, false, urlPatterns);
    }

    /**
     * @param servletContext servlet context
     * @param urlPatterns url pattern array
     */
    public static void addSpringWebAppFilter(ServletContext servletContext, String... urlPatterns) {
        servletContext.addFilter(Codecs.uuid(), SpringWebAppFilter.class).addMappingForUrlPatterns(null, false, urlPatterns);
    }
}
